/*******************************************************************************
 * Copyright (c) 2012-2017 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/
package org.eclipse.che.ide.statepersistance;

import elemental.json.Json;
import elemental.json.JsonException;
import elemental.json.JsonFactory;
import elemental.json.JsonObject;

import com.google.inject.Inject;
import com.google.inject.Singleton;

import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.ide.api.component.StateComponent;
import org.eclipse.che.ide.api.preferences.PreferencesManager;
import org.eclipse.che.ide.util.loging.Log;

import java.util.Map;

/**
 * Responsible for persisting and restoring IDE state across sessions.
 * Uses user preferences as storage for serialized state.
 *
 * @author Artem Zatsarynnyi
 * @author Yevhen Vydolob
 */
@Singleton
public class AppStateManager {

    /** The name of the property for the mappings in user preferences. */
    public static final String PREFERENCE_PROPERTY_NAME = "IdeAppStates";

    private static final String WORKSPACE = "workspace";

    private final Map<String, StateComponent> persistenceComponents;
    private final PreferencesManager          preferencesManager;
    private final JsonFactory                 jsonFactory;
    private       JsonObject                  allWsState;

    @Inject
    public AppStateManager(Map<String, StateComponent> persistenceComponents,
                           PreferencesManager preferencesManager,
                           JsonFactory jsonFactory) {
        this.persistenceComponents = persistenceComponents;
        this.preferencesManager = preferencesManager;
        this.jsonFactory = jsonFactory;
        readStateFromPreferences();
    }

    private void readStateFromPreferences() {
        final String json = preferencesManager.getValue(PREFERENCE_PROPERTY_NAME);
        if (json == null) {
            allWsState = jsonFactory.createObject();
        } else {
            try {
                allWsState = jsonFactory.parse(json);
            } catch (Exception e) {
                // create 'clear' state if any deserializing error occurred
                allWsState = jsonFactory.createObject();
            }
        }
    }

    public void restoreWorkspaceState(String wsId) {
        if (allWsState.hasKey(wsId)) {
            restoreState(allWsState.getObject(wsId));
        }
    }

    private void restoreState(JsonObject settings) {
        try {
            if (settings.hasKey(WORKSPACE)) {
                JsonObject workspace = settings.getObject(WORKSPACE);
                for (String key : workspace.keys()) {
                    if (persistenceComponents.containsKey(key)) {
                        StateComponent component = persistenceComponents.get(key);
                        component.loadState(workspace.getObject(key));
                    }
                }
            }
        } catch (JsonException e) {
            Log.error(getClass(), e);
        }
    }

    public Promise<Void> persistWorkspaceState(String wsId) {
        final JsonObject settings = Json.createObject();
        JsonObject workspace = Json.createObject();
        settings.put(WORKSPACE, workspace);
        for (Map.Entry<String, StateComponent> entry : persistenceComponents.entrySet()) {
            try {
                String key = entry.getKey();
                workspace.put(key, entry.getValue().getState());
            } catch (Exception e) {
                Log.error(getClass(), e);
            }
        }
        allWsState.put(wsId, settings);
        return writeStateToPreferences(allWsState);
    }

    private Promise<Void> writeStateToPreferences(JsonObject state) {
        final String json = state.toJson();
        preferencesManager.setValue(PREFERENCE_PROPERTY_NAME, json);
        return preferencesManager.flushPreferences().catchError(new Operation<PromiseError>() {
            @Override
            public void apply(PromiseError arg) throws OperationException {
                Log.error(AppStateManager.class, "Failed to store app's state to user's preferences");
            }
        });
    }

    public boolean hasStateForWorkspace(String wsId) {
        return allWsState.hasKey(wsId);
    }

}
